package com.rychlickikicior.kitijdb;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author kitikat
 */
class XMLSettingsReader extends SettingsReader {
    private InputStream is;
    private Element document = null;

    public XMLSettingsReader(InputStream is) {
        this.is = is;
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            this.document = db.parse(is).getDocumentElement();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        }
          catch (Exception saxe) {
              saxe.printStackTrace();
        }
    }

    private Node getFirstChildWithName(Node parent, String name) {
        NodeList children = parent.getChildNodes();
        for (int i=0;i<children.getLength();i++) {
            Node child = children.item(i);
            if (child.getNodeName() != null && child.getNodeName().equals(name))
                return child;
        }
        return null;
    }

    private Query retrieveQuery(Node query) {
        Query result = new Query();
        result.setName(this.getFirstChildWithName(query, "name").getTextContent().trim());
        result.setType(QueryType.valueOf(this.getFirstChildWithName(query, "type").getTextContent().trim()));
        result.setContent(this.getFirstChildWithName(query, "content").getTextContent().trim());
        return result;
    }

    private Class retrieveClassFromChild(Node child) {
        Node classNode = this.getFirstChildWithName(child, "class");
        String className = classNode.getTextContent().trim();
        try {
            return Class.forName(className);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Class retrievePropertyTypeFromChild(Node child) {
        Node classNode = this.getFirstChildWithName(child, "type");
        String className = classNode.getTextContent().trim();
        try {
            return Class.forName(className);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private String retrievePropertyFromChild(Node child) {
        Node propertyNode = this.getFirstChildWithName(child, "property");
        String propertyName = propertyNode.getTextContent().trim();
        return propertyName;
    }

    private Descriptor getDescriptorFromEntity(Node entity) {
        Descriptor result = new Descriptor();
        result.setClassObject(this.getClassFromEntity(entity));
        Node version = this.getFirstChildWithName(entity, "version");
        result.setVersion(version != null);
        Node children = this.getFirstChildWithName(entity, "children");
        if (children != null) {
            NodeList childList = children.getChildNodes();
            for (int i=0;i<childList.getLength();i++) {
                Node child = childList.item(i);
                if (child.getNodeName() != null && child.getNodeName().equals("child")) {
                    Class c = this.retrieveClassFromChild(child);
                    String v = this.retrievePropertyFromChild(child);
                    Class propType = this.retrievePropertyTypeFromChild(child);
                    result.addChild(c, v, propType);
                }
            }
        }
        NodeList queries = this.getFirstChildWithName(entity, "queries").getChildNodes();
        List<Query> queriesList = new ArrayList<Query>(queries.getLength());
        for (int i=0;i<queries.getLength();i++) {
            Node query = queries.item(i);
            if (query.getNodeName()!=null && query.getNodeName().equals("query"))
                queriesList.add(this.retrieveQuery(query));
        }
        result.setQueries(queriesList);
        return result;
    }

    private Class getClassFromEntity(Node entity) {
        Node entityName = this.getFirstChildWithName(entity, "name");
        Class c = null;
        try {
            c = Class.forName(entityName.getTextContent().trim());
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return c;
    }

    private QueryData getQueryDataFromEntities(Element root) {
        Node entities = this.getFirstChildWithName(root, "entities");
        NodeList entityObjects = entities.getChildNodes();
        QueryData result = new QueryData();
        for (int i=0;i<entityObjects.getLength();i++) {
            Node entity = entityObjects.item(i);
            if (entity.getNodeName()!=null && entity.getNodeName().equals("entity")) {
                result.put(this.getClassFromEntity(entity), this.getDescriptorFromEntity(entity));
            }
        }
        return result;
    }

    @Override
    public QueryData getQueryData() {
        return (this.document != null ? this.getQueryDataFromEntities(this.document) : null);
    }

    @Override 
    public Properties getProperties() {
        if (this.document == null)
            return null;
        Properties result = new Properties();
        NodeList properties = this.getFirstChildWithName(this.document, "properties").getChildNodes();
        for (int i=0;i<properties.getLength();i++) {
            Node node = properties.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE)
                result.put(node.getNodeName(), node.getTextContent());
        }
        return result;
    }
}
